#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512

int main() 
{
    /* 初始化 Winsock */
    WSADATA wsaData;
    int iResult;

    // 调用 WSAStartup 函数以启用 WS2_32.dll 库
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if(iResult != 0)
    {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    /* 创建套接字 */
    addrinfo *result = nullptr, *ptr = nullptr, hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; // IPv4
    hints.ai_socktype = SOCK_STREAM; // TCP
    hints.ai_protocol = IPPROTO_TCP; // TCP
    //hints.ai_flags = AI_PASSIVE; // 允许使用本地地址

    // 调用 getaddrinfo 函数以获取服务器的地址信息
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if(iResult != 0)
    {
        std::cout << "getaddrinfo failed: " << iResult << std::endl;
        WSACleanup();
        return 1;
    }

    // 创建套接字对象
    SOCKET connectSocket = INVALID_SOCKET;

    // 不断遍历地址信息列表，直到找到可用的地址
    for(ptr=result; ptr != NULL; ptr = ptr->ai_next)
    {
        // 创建套接字对象
        connectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if(connectSocket == INVALID_SOCKET)
        {
            std::cout << "sockedt failed with error: " << WSAGetLastError() << std::endl;
            WSACleanup();
            return 1;
        }
        std::cout << "socket created" << std::endl;

        /* 连接到服务器 */
        iResult = connect(connectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if(iResult == SOCKET_ERROR)
        {
            closesocket(connectSocket);
            connectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    // 连接成功后，不再需要地址信息，所以释放地址信息
    freeaddrinfo(result);
    
    if(connectSocket == INVALID_SOCKET)
    {
        std::cout << "Unable to connect to server!" << std::endl;
        WSACleanup();
        return 1;
    }

    /* 客户端发送和接收数据 */
    int recvbuflen = DEFAULT_BUFLEN; // 指定要接收的字节数
    const char* sendbuf = "this is a test message"; // 要发送的数据
    char recvbuf[DEFAULT_BUFLEN]; // 接收缓冲区
    int iSendResult; // 发送结果
    int iRecvResult; // 接收结果

    // 向服务端发送数据
    // 返回发送的字节数或错误值
    iSendResult = send(connectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if(iSendResult == SOCKET_ERROR)
    {
        std::cout << "send failed with error: " << WSAGetLastError() << std::endl;
        closesocket(connectSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Bytes sent: " << iSendResult << std::endl;

    // 关闭发送连接
    iResult = shutdown(connectSocket, SD_SEND);
    if(iResult == SOCKET_ERROR)
    {
        std::cout << "shutdown failed with error: " << WSAGetLastError() << std::endl;
        closesocket(connectSocket);
        WSACleanup();
        return 1;
    }

    // 接收服务端返回的数据
    do{
        iRecvResult= recv(connectSocket, recvbuf, recvbuflen, 0);

        if(iRecvResult > 0)
        {
            std::cout << "Bytes received: " << iRecvResult << std::endl;
            std::cout << "Received message from server: " << recvbuf << std::endl;
        }
        else if(iRecvResult == 0)
        {
            std::cout << "Connection closed" << std::endl;
        }
        else
        {
            std::cout << "recv failed with error: " << WSAGetLastError() << std::endl;
        }

    }while (iRecvResult > 0);
    
    // 完全关闭套接字，并释放资源
    closesocket(connectSocket);
    WSACleanup();

    std::cout << "client: bye"<< std::endl;
    return 0;
}